Version 1.7.3 of MacZoop had an enhanced version of ZDialog. This class is 100% backward compatible with older code that uses ZDialog, but offers some very useful new features. This information will be put into the manual in due course, but is presented as a technote so it can be used straight away.
Enabling and Disabling items.
Previously, only controls could be disabled or enabled individually using the HiliteItem() method. However, there are two new methods, EnableItem() and DisableItem() which work for both controls and editable text fields. These methods also allow whole groups of controls to be enabled and disabled in one go (see Groups, below). An editable text field, when disabled, is converted to a static text item so it can no longer accept typing. In addition, the item is drawn with its text in 50% grey, and the border is drawn in grey (or grey pattern for a black & white dialog). If the field was the current field when DisableItem() is called, another field is selected. You can't normally convert a static text item to become editable in this way.
Edit Field filtering and range checking.
Standard edit fields accept whatever you type into them. Previously, if you wanted to restrict what could be typed into a field (for example restricting it to numbers only), you had to override Type() and check the target of the keystroke and the character to see if it was legal. Similarly, if you wanted to check that a numeric value entered into a field fell within a certain range, you had to override CloseDialog() and perform the tests. ZDialog now provides these features as standard, and supports an easy way to set up such fields in your resources.
Under program control, you can call SetEditFieldInfo() to supply some behaviour flags and optionally a minimum and maximum acceptable value for the field. The flags passed are in fact a bitfield, but only certain combinations make sense. They are presented here as decimal numbers which is how you will set them up in your resources (see below).
No special restrictions- pass 0 or do nothing.
To allow only the numbers 0 through to 9 and nothing else: pass 8. Suitable for entry of unsigned integers.
To allow only the numbers 0 to 9, and also a minus sign, pass 2. Suitable for entry of signed integers.
To allow signed floating point numbers to be entered, pass 4. This permits numbers, the - sign and the decimal point.
To allow only positive floating point numbers to be entered, pass 16.
To allow only letters to be entered, pass 128. This rejects all characters with an ascii code less than the '@' symbol and greater than a zero. This is perhaps suitable for a URL entry field, for example.
Keystrokes that are not valid result in a beep. You could override KeyIsLegal(), a new method, to extend or modify this standard filtering behaviour. Note that pasting text into a field also checks for illegal characters in the text to be pasted and will not paste if the field won't accept the characters. This also relies on the KeyIsLegal() method, so a single override will work for both.
If you have a numeric entry field and want to set up a certain minimum or maximum value for the entry, you can pass one or both of the following flags in addition to the ones above:
editFieldHasMinValue (32)- lower bounds is checked
editFieldHasMaxValue (64)- upper bounds is checked
Note that the checking code assumes that the entry is an integer- this won't work properly if the field contains a floating point value. The range is checked at CloseDialog() time- i.e. when the OK button is clicked. If the range check fails, an alert is displayed that shows what the limits are, and on closing this, the offending field is selected and the value set to the nearest limit. Fields are not checked if the dialog is cancelled, or if the field is disabled at the time the OK button was clicked. A new 'ALRT' resource ID=137 is required.
Setting up fields from the DITL resource.
You can call SetEditFieldInfo() from your code, or more simply set up the resources in a certain way. This follows a similar technique to that for radio groups. The field initial text string is formatted thus:
[default text][//<flags>[,<min>][,<max>]]
Items in square brackets are optional. As with radio buttons, the default text (if any) is followed by two forward slashes which delimit the meta-data. The first item is the behaviour flags as listed above, e.g. 98 for fully limit-checked signed integer field (32 + 64 + 2). If the flags indicate a limit, the next one or two items, delimited by a comma, give the limits. Note that if you only want a max limit, you still need to supply a min limit here, though if the associated flag is not set, it's value is ignored, so you can simply pass 0. If any parameter is missing but the flags indicate that they are wanted, the value will be taken as 0.
If you use GetEditFieldInfo() and SetEditFieldInfo() from your own code, you must take care, when manipulating the flags value, that you only change the bits of the flags you need to and leave others untouched. This is particularly important if the field is disabled, since this fact is recorded using bit 0 of these flags.
Groups.
The concept of a group ID, previously only associated with radio buttons, has been extended to all controls. This does not affect radio buttons at all- they work exactly as before. The advantage of this extension is that entire groups of items can be disabled or enabled at once with a single call, rather than having to go through and do this one by one. This convenient ability ONLY applies to controls- you can't assign a group ID to an edit text field. To disable a group, call DisableItem() with the negative of the group ID. To re-enable it, call EnableItem() using the negative group ID. Note that if some controls in the group are already disabled when you disable the group as a whole, only the originally enabled items are re-enabled when EnableItem() is called. The state history of items is stacked to a maximum of 32 bits- what this means is that if you disable a group, the next enable restores the previous state, and the one after that restores the state before that. In other words, Enable/Disable work as matched pairs for groups. You can pass 0 to these methods to treat all controls as belonging to a single group, thus disabling or enabling everything. This is now done by the Activate/Deactivate methods, and because the state history is stacked, you will get a very nice activation behaviour where an inactive dialog has all controls dimmed, but the exact previous state is restored when the dialog is reactivated.
To further enhance the group dimming feature, you should be aware that only the low 8 bits of the group ID are compared when performing this operation. This allows different groups to be treated as a single group for the purpose of disabling or enabling items, if this is useful. For example, you might have a section of dialog containing two different radio button groups, but want to disable them both with one call. If one has a group ID of 1 and the other 257 (256 + 1), they will both be disabled when calling DisableItem( -1 ), but will work independently when clicked. This idea can be extended by using group IDs (in this example) of 513, 769, etc so is not restricted to pairs.
More complex behaviours than this require that you code them as such, but this should now be easier than it was.
Extending Dialogs.
ZDialog now encompasses the ability to extend and shorten dialogs using the AppendDITL and ShortenDITL toolbox functions, but enhances this to allow MacZoop features like control grouping and easy user-item handling. The new methods are AppendItemsToDialog(), and RemoveAppendedItems(). The appended 'DITL' can use MacZoop grouping conventions for controls and the new edit field behaviour scheme and will work as expected. Note that group IDs in the appended items are treated as part ofthe dialog as a whole, so a group ID of 1 in the extra part is considered part of any group ID 1 in the main part. Thus you need to ensure that group IDs are unique across all the appended DITL resources unless you deliberately want to take advantage of the fact that you can effectively extend groups this way. The RemoveAppendedItems() method always returns the dialog to its original items only, though will not resize the dialog if it was enlarged by an earlier AppendItems... call. AppendItemsToDialog() can be called more than once if necessary to add several parts to a dialog.
Note that the System 7 bug relating to 'ictb' resources has not been addressed by these methods- you are advised not to use 'ictb' resources with multi-part dialogs unless you are definitely only going to run on System 8.0 or later, which resolves the problem.
Other Enhancements.
The "Select All" command now works for dialog editable text fields.
Some previously protected members are now public. These include FakeClick(), GetItemType(), GetItemBounds(), HasEditFields() and FindItem(). This may prove useful.
ClickItem() now transmits a new message for every click, <kMsgDialogItemClicked>. The message data is a pointer to a long where the high word is the dialog ID, and the low word is the item ID clicked. The benefit of this is that the dialog's boss can implement click responses for most typical dialogs without the need to override the dialog itself, which may lead to simpler code architectures for many typical cases.
Some internal methods and data structures have been changed to assist with the implementation of the new features. As always, your code should not rely on the format of internal data structures- in particular, the way groups are handled has been totally changed- if your code relied on the contents of the old rGroupList handle, it will almost certainly crash.
A DrawOneItem() method has been added which allows you to redraw a single dialog item straight away without having to wait for an update or writing code to fake out calls to Draw().
A SetDialogBaseFont() method has been added which allows you to specify a font, size and style for the dialog as a whole. This is overridden for items that use the 'ictb' scheme, but is very handy if you don't use 'ictb's yet want a different basic dialog font. This affects only static and edit text items- controls will use the system font unless you take other steps to change this. You should call this method after InitZWindow() but before the dialog is made visible.
Modal dialogs are now created in front of all other windows, whereas before they were created at the back and immediately moved to the front. Normally this was not noticeable but was slightly disturbing on very slow Macs.